﻿using Microsoft.Xna.Framework;
using System;
namespace DDOS.Camera
{
    public class ThirdPersonCamera3D : Camera3D
    {
        public const float NO_MIN = float.MinValue;
        public const float NO_MAX = float.MaxValue;

        public const float ZOOM_MIN = -25f;
        public const float ZOOM_MAX = -3f;

        Vector3 startingDistance;

        public override Vector3 Target
        {
            get
            {
                return base.Target;
            }
            set
            {
                base.Target = value;
                positionNeedsUpdating = true;
            }
        }

        protected bool positionNeedsUpdating = false;
        public override Vector3 Position
        {
            get
            {
                if (positionNeedsUpdating)
                {
                    position = Vector3.Transform(startingDistance,
                                                    Matrix.CreateFromYawPitchRoll(Rotation.Y, Rotation.X, Rotation.Z) *
                                                    Matrix.CreateTranslation(Target));
                }
                return position;
            }
            set
            {
                position = value;
            }
        }

        private Vector3 rotation;
        private bool rotationNeedsUpdating;
        /// <summary>
        /// The rotation of the camera relative to its target and its starting position.
        /// </summary>
        public Vector3 Rotation
        {
            get
            {
                if (rotationNeedsUpdating)
                {
                    rotationNeedsUpdating = false;

                    Vector3 newRotation = Rotation;

                    if (newRotation.X > MathHelper.TwoPi)
                    {
                        newRotation.X += MathHelper.TwoPi;
                    }
                    else if (newRotation.X < -MathHelper.TwoPi)
                    {
                        newRotation.X -= MathHelper.TwoPi;
                    }
                    if (newRotation.Y > MathHelper.TwoPi)
                    {
                        newRotation.Y += MathHelper.TwoPi;
                    }
                    else if (newRotation.Y < -MathHelper.TwoPi)
                    {
                        newRotation.Y -= MathHelper.TwoPi;
                    }
                    if (newRotation.Z > MathHelper.TwoPi)
                    {
                        newRotation.Z += MathHelper.TwoPi;
                    }
                    else if (newRotation.Z < -MathHelper.TwoPi)
                    {
                        newRotation.Z -= MathHelper.TwoPi;
                    }

                    if (Rotation.X > MaxRotationValues.X)
                    {
                        newRotation.X = MaxRotationValues.X;
                    }
                    else if (Rotation.X < MinRotationValues.X)
                    {
                        newRotation.X = MinRotationValues.X;
                    }
                    if (Rotation.Y > MaxRotationValues.Y)
                    {
                        newRotation.Y = MaxRotationValues.Y;
                    }
                    else if (Rotation.Y < MinRotationValues.Y)
                    {
                        newRotation.Y = MinRotationValues.Y;
                    }
                    if (Rotation.Z > MaxRotationValues.Z)
                    {
                        newRotation.Z = MaxRotationValues.Z;
                    }
                    else if (Rotation.Z < MinRotationValues.Z)
                    {
                        newRotation.Z = MinRotationValues.Z;
                    }

                    rotation = newRotation;
                }
                return rotation;
            }
            set
            {
                rotationNeedsUpdating = true;
                positionNeedsUpdating = true;
                rotation = value;
            }
        }

        /// <summary>
        /// The maximum values at which the camera can rotate about the x, y, and z axes.
        /// </summary>
        public Vector3 MaxRotationValues
        {
            get;
            set;
        }

        /// <summary>
        /// The minimum values at which the camera can rotate about the x, y, and z axes.
        /// </summary>
        public Vector3 MinRotationValues
        {
            get;
            set;
        }

        public Vector3 Distance
        {
            get
            {
                return startingDistance;
            }
            set
            {
                Vector3 newVal = new Vector3(MathHelper.Clamp(value.X, ZOOM_MIN, ZOOM_MAX), MathHelper.Clamp(value.Y, ZOOM_MIN, ZOOM_MAX), MathHelper.Clamp(value.Z, ZOOM_MIN, ZOOM_MAX));
                startingDistance = newVal;
            }
        }
        /// <summary>
        /// Creates a new third person camera.
        /// </summary>
        /// <param name="startingDistance">The distance from the camera's target</param>
        /// <param name="startingRotation">The initial rotation of the camera around the x, y, and z axes</param>
        /// <param name="target">The target of the camera, the point it's looking at</param>
        /// <param name="minRotationValues">The minimum rotation values of the camera</param>
        /// <param name="maxRotationValues">The maximum rotation values of the camera</param>
        /// <param name="projectionMatrix">The projection matrix used by the camera</param>

        public ThirdPersonCamera3D(float startingDistance, Vector3 startingRotation, Vector3 target, Vector3 minRotationValues, Vector3 maxRotationValues, Matrix projectionMatrix) :
            base(new Vector3(0, 0, startingDistance), target, Vector3.Up, projectionMatrix)
        {
            MinRotationValues = minRotationValues;
            MaxRotationValues = maxRotationValues;
            this.startingDistance = Target - new Vector3(0, 0, startingDistance);
            Rotation = startingRotation;
        }
    }
}
